作為 【Day 4】 使用 Elemental MediaConvert 轉檔 的功能實踐,本篇將說明串接 API Gateway 達到透過 Web UI 一鍵觸發轉檔!!
mov
,作為 iPhone 預設綠影格式,要擺到瀏覽器中播放查看,需要轉換成 mp4
import os, json, boto3, jwt
SECRET = os.environ.get("JWT_SECRET", "mysecret")
BUCKET_NAME = os.environ.get("BUCKET_NAME", "exsky-backup-media")
REGION = os.environ.get("AWS_REGION", "ap-northeast-1")
mediaconvert = boto3.client("mediaconvert", region_name=REGION)
def lambda_handler(event, context):
# --- JWT 驗證 ---
headers = event.get("headers", {}) or {}
auth = headers.get("authorization") or headers.get("Authorization") or ""
if not auth.startswith("Bearer "):
return _unauthorized("Missing token")
token = auth.split(" ")[1]
try:
decoded = jwt.decode(token, SECRET, algorithms=["HS256"])
username = decoded.get("username", "unknown")
except Exception:
return _unauthorized("Invalid token")
# --- 解析 body ---
try:
body = json.loads(event.get("body", "{}"))
key = body.get("key")
if not key:
return _bad_request("Missing key")
except Exception:
return _bad_request("Invalid body")
# --- 準備 MediaConvert Job ---
try:
# 找到帳號專屬的 MediaConvert endpoint
endpoints = mediaconvert.describe_endpoints(MaxResults=1)
mediaconvert_ep = boto3.client("mediaconvert", endpoint_url=endpoints["Endpoints"][0]["Url"], region_name=REGION)
job_settings = {
"Inputs": [
{
"FileInput": f"s3://{BUCKET_NAME}/{key}",
"AudioSelectors": {
"Audio Selector 1": {"DefaultSelection": "DEFAULT"}
},
"VideoSelector": {}
}
],
"OutputGroups": [
{
"Name": "File Group",
"OutputGroupSettings": {
"Type": "FILE_GROUP_SETTINGS",
"FileGroupSettings": {
"Destination": f"s3://{BUCKET_NAME}/{username}/converted/"
}
},
"Outputs": [
{
"ContainerSettings": {"Container": "MP4"},
"VideoDescription": {
"CodecSettings": {
"Codec": "H_264",
"H264Settings": {"Bitrate": 5000000, "RateControlMode": "CBR"}
}
},
"AudioDescriptions": [
{"CodecSettings": {"Codec": "AAC", "AacSettings": {"Bitrate": 96000}}}
]
}
]
}
]
}
resp = mediaconvert_ep.create_job(
Role=os.environ["MEDIACONVERT_ROLE"], # IAM role ARN
Settings=job_settings
)
return {
"statusCode": 200,
"headers": {"Access-Control-Allow-Origin": "https://vlog.nipapa.tw"},
"body": json.dumps({"jobId": resp["Job"]["Id"]})
}
except Exception as e:
return _server_error(str(e))
# ---- Helpers ----
def _unauthorized(msg):
return {
"statusCode": 401,
"headers": {"Access-Control-Allow-Origin": "https://vlog.nipapa.tw"},
"body": json.dumps({"error": msg})
}
def _bad_request(msg):
return {
"statusCode": 400,
"headers": {"Access-Control-Allow-Origin": "https://vlog.nipapa.tw"},
"body": json.dumps({"error": msg})
}
def _server_error(msg):
return {
"statusCode": 500,
"headers": {"Access-Control-Allow-Origin": "https://vlog.nipapa.tw"},
"body": json.dumps({"error": msg})
}
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
},
{
"Effect": "Allow",
"Principal": {
"Service": "mediaconvert.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}